数据结构(C语言) —— 线性表(链表)

前言

上一篇博客我们详细的讲述了顺序表的实现,但以讲述的形式来记录数据结构这部分的笔记效率实在是有些低,所以接下来的部分我就单纯地列出知识点就完事了。嘻嘻嘻!~

单链表

单链表结点的结构体:

typedef struct Node
{
    DataType data;
    struct Node *next;
} SLNode;
其中,data域用来存放数据元素,next域用来存放指向下一结点的指针。

单链表还分为带头结点结构和不带头结点结构两种。我们把指向单链表的指针称作头指针,头指针所指的不存放数据元素的第一个结点称作头结点。我们一般构造带头结点的单链表(以下讲解的也是带头结点的单链表)。

单链表的操作实现

1.C语言的动态申请内存空间函数

要知道,单链表中的每一个结点,是在需要时才向系统申请的,这称作动态内存空间申请。动态申请的内存空间,当不再需要时,必须手动释放。C语言提供了动态申请内存空间的函数malloc()和动态释放函数内存空间的函数free()。这些函数包含在头文件malloc.h中。

void *malloc(unsigned size);
-> 向系统动态申请size个字节的内存单元空间,函数返回值为所申请内存空间的首地址。

void free(void *p);
-> p为内存空间首地址指针。

sizeof(<以定义的数据类型>)
-> 计算所需内存空间的大小。

2.单链表的操作实现

1.初始化ListInitiate(SLNode **head)

void ListInitiate(SLNode **head)        //初始化
{
    *head = (SLNode *)malloc(sizeof(SLNode));    //申请头结点,由head指示其地址
    (*head)->next = NULL;                //置结束标记NULL
}

2.求当前数据元素个数ListLength(SLNode *head)

int ListInitiate(SLNode *head)
{
    SLNode *p = head;            //p指向头结点
    int size = 0;                //size初始化为0

    while(p->next!=NULL)        //循环计数
    {
        p = p->next;
        size ++;
    }
    return size;
}

3.插入ListInsert(SLNode *head, int i, DataType x)

int ListInsert(SLNode *head, int i, DataType x)
//在带头结点的单链表head的第i(0<=i<=size)个结点前
//插入一个存放数据元素x的结点。插入成功返回1,失败返回0。
{
    SLNode *p, *q;
    int j;

    p = head;
    j = -1;
    while(p->next!=NULL && j<i-1)
    //最终让p指向第i-1个结点
    {
        p = p->next;
        j++;
    }

    if(j!=i-1)
    {
        printf("插入位置参数错误!\n");
        return 0;
    }

    q = (SLNode *)malloc(sizeof(SLNode));    //生成新的结点
    q->data = x;                            //新节点数据域赋值

    q->next = p->next;                        //插入
    p->next = q;
    return 1;
}

4.删除ListDelete(SLNode head, int t, DataType x)

int ListDelete(SLNode *head, int i, DataType *x)
//删除带头结点单链表head的第i(0<=i<=size-1)个结点
//被删除结点的数据域值由x带回。删除成功返回1,失败返回0。
{
    SLNode *p, *s;
    int j;
    p = head;
    j = -1;
    while(p->next!=NULL && p->next->next!=NULL && j<i-1)
    //最终p指向第i-1个结点
    {
        p = p->next;
        j++;
    }

    if(j!=i-1)
    {
        printf("删除位置参数错误!\n");
        return 0;
    }

    s = p->next;                //指针s指向第i个结点
    *x = s->data;                //赋值
    p->next = p->next->next;    //删除
    free(s);                    //释放内存空间
    return 1;
}

5.取数据元素ListGet(SLNode head, int i, DataType x)

int ListGet(SLNode *head, int i, DataType *x)
{
    SLNode *p;
    int j;

    p = head;
    j = -1;
    while(p->next!=NULL && j<i)
    {
        p = p->next;
        j++;
    }

    if(j!=i)
    {
        printf("取元素位置参数错误!\n");
        return 0;
    }

    *x = p->data;
    return 1;
}

循环单链表

链表的最后一个结点的指针域不再是结束标记,而是指向整个链表的第一个结点,从而使链表形成一个环。(与单链表的实现差别不大,不做讨论。)

双向链表

双向链表也有循环和非循环两种结构,这里讨论带头节点的双向循环链表。
双向循环链表结点的结构体定义如下:

typedef struct Node
{
    DataType data;         //数据域
    struct Node *next;     //指向后驱结点的指针
    struct Node *prior;    //指向前驱结点的指针
} DLNode;

双向循环链表的操作实现

1.初始化

void ListInitiate(DLNode **head)
{
    *head = (DLNode *)malloc(sizeof(DLNode));
    (*head)->prior = *head;
    (*head)->next = *head;
}

2.插入数据元素

int ListTnsert(DLNode *head, int i, DataType x)
//在带头结点的双向循环链表head的第i(0<=i<=size)个结点前
//插入一个存放数据元素x的结点。成功返回1,失败返回0。
{
    DLNode *p, *s;
    int j;

    p = head->next;
    j = 0;
    while(p->next!=head && j<i)            //寻找第i个结点
    {
        p = p->next;
        j++; 
    }

    if(j!=i)
    {
        printf("插入位置参数出错!");
        return 0;
    }

    s = (DLNode *)malloc(sizeof(DLNode));
    s->data = x;

    s->prior = p->prior;            //插入步骤
    p->prior->next = s;
    s->next = p;
    p->prior = s;

    return 1;
}

3.删除数据元素

int ListDelete()
//删除带头结点双向循环链表head的第i(0<=i<=size-1)个结点
//被删除的结点的数据元素域值由x带回。成功返回1,失败返回0。
{
    DLNode *p;
    int j;

    p = head->next;
    j = 0;
    while(p->next!=head && j<i)
    {
        p = p->next;
        j++;
    }

    if(j!=i)
    {
        printf("删除位置参数错误!");
        return 0;
    }

    p->prior->next = p->next;
    p->next->prior = p->prior;

    free(p);
    return 1;
}

4.求当前数据元素个数

int ListLength(DLNode *head)
{
    DLNode *p = head;
    int size = 0;

    while(p->next != head)
    {
        p = p->next;
        size ++;
    }
    return size;
}

5.撤销内存空间

void Destory(DLNode **head)
{
    DLNode *p, *p1;
    int i, n = ListLength(*head)

    p = *head;
    for(i = 0, i<=n, i++)
    {
        p1 = p;
        p = p->next;
        free(p1);
    }

    *head = NULL;
}